
;**********************************************************************************
;									常量
;**********************************************************************************
;----------------------------------------------------------------------------------
;有关内核外设寄存器定义可参考官方文档： STM32F10xxx Cortex-M3 programming manual
;系统控制块外设 SCB 地址范围： 0xE000ED00-0xE000ED3F
;----------------------------------------------------------------------------------
SCB_VTOR        EQU     0xE000ED08               ; Vector Table Offset Register 向量表偏移寄存器
NVIC_INT_CTRL   EQU     0xE000ED04               ; interrupt control state register 中断控制状态寄存器
NVIC_SYSPRI2    EQU     0xE000ED20               ; system priority register (2) 系统优先级寄存器(2)
NVIC_PENDSV_PRI EQU     0x00FF0000               ; PendSV priority value (lowest) PendSV 优先级值 (lowest)
NVIC_PENDSVSET  EQU     0x10000000               ; value to trigger PendSV exception 触发 PendSV exception 的值

;**********************************************************************************
;									代码生成指令
;**********************************************************************************
    AREA |.text|, CODE, READONLY, ALIGN=2
    THUMB
    REQUIRE8
    PRESERVE8

;**********************************************************************************
;									全局变量
;**********************************************************************************
    IMPORT rt_thread_switch_interrupt_flag
    IMPORT rt_interrupt_from_thread
    IMPORT rt_interrupt_to_thread

;/*
; * rt_base_t rt_hw_interrupt_disable();
; */
rt_hw_interrupt_disable    PROC
    EXPORT  rt_hw_interrupt_disable
    MRS     r0, PRIMASK
    CPSID   I
    BX      LR
    ENDP

;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
rt_hw_interrupt_enable    PROC
    EXPORT  rt_hw_interrupt_enable
    MSR     PRIMASK, r0
    BX      LR
    ENDP


;----------------------------------------------------------------------
; void PendSV_Handler(void);
; r0 --> switch from thread stack
; r1 --> switch to thread stack
; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
;----------------------------------------------------------------------
PendSV_Handler   PROC
	;导出 PendSV_Handler，实际是中断向量声明
    EXPORT PendSV_Handler

    ; disable interrupt to protect context switch，失能中断，为了保护上下文切换不被打断
	;PRIMASK 这是个只有单一比特的寄存器。 在它被置 1 后，就关掉所有可屏蔽的异常，只剩下 NMI 和硬 FAULT 可以响应。它的缺省值是 0，表示没有关中断。
    MRS     r2, PRIMASK		;读取PRIMASK中寄存器值 r2 
    CPSID   I		;PRIMASK=1 ,关闭中断指令，除了 NMI 和硬 FAULT ,中断都不响应

    ; get rt_thread_switch_interrupt_flag,其在rt_hw_context_switch_to函数中置1
	; r1 = rt_thread_switch_interrupt_flag;
	; if(rt_thread_switch_interrupt_flag == 0)goto pendsv_exit;
    LDR     r0, =rt_thread_switch_interrupt_flag	; r0 = &rt_thread_switch_interrupt_flag;
    LDR     r1, [r0]		; r1 = *r0;
    CBZ     r1, pendsv_exit         ; pendsv already handled，如果 r1 值为0，则跳转 pendsv_exit ，即退出中断函数

	; clear rt_thread_switch_interrupt_flag to 0 ，rt_thread_switch_interrupt_flag 不为0,需要清零
	; rt_thread_switch_interrupt_flag = 0;
    MOV     r1, #0x00		; r1 = 0;
    STR     r1, [r0]		; *r0 = r1;

	; 加载 rt_interrupt_from_thread 指针指向的值到 r1
	; 判断 r1 是否为 0，为 0 则跳转到 switch_to_thread
	; 第一次线程切换时 rt_interrupt_from_thread 肯定为 0，会直接跳转到 switch_to_thread
    LDR     r0, =rt_interrupt_from_thread	; r0 = &rt_interrupt_from_thread;
    LDR     r1, [r0]	; r1 = *r0; => r1 = rt_interrupt_from_thread;
    CBZ     r1, switch_to_thread    ; skip register save at the first time
	
	;存在 rt_interrupt_from_thread ，需要保存上文(上一线程运行环境)
	; ========================== 上文保存 ========================
	; 当进入 PendSVC Handler 时，上一个线程运行的环境即：
	; xPSR， PC（ 线程入口地址）， R14， R12， R3， R2， R1， R0（ 线程的形参）
	; 这些 CPU 寄存器的值会自动保存到线程的栈中，剩下的 r4~r11 需要手动保存
	; 获取线程栈指针到 r1
    MRS     r1, psp                 ; get from thread stack pointer ,读取psp线程栈指针到 r1 ,r1 = psp;
	;将 CPU 寄存器 r4~r11 的值存储到 r1 指向的地址(每操作一次地址将递减一次)
    STMFD   r1!, {r4 - r11}         ; push r4 - r11 register ,将 r4-r11寄存器数据push存储到 from_thread 线程栈指针(当前psp)地址，r1地址值自减32 bytes
    LDR     r0, [r0]				; r0 = rt_interrupt_from_thread;
    STR     r1, [r0]                ; update from thread stack pointer 用psp-32值更新上一个线程栈指针 , *rt_interrupt_from_thread = r1;
	
	; ========================== 下文切换 ==========================
switch_to_thread
    LDR     r1, =rt_interrupt_to_thread		; r1 = &rt_interrupt_to_thread;
    LDR     r1, [r1]				; r1 = rt_interrupt_to_thread;
    LDR     r1, [r1]                ; load thread stack pointer. r1 = *rt_interrupt_to_thread; => r1 = to_thread->sp;

    LDMFD   r1!, {r4 - r11}         ; pop r4 - r11 register.从 to_thread 线程栈指针sp地址pop取出8个rt_uint32_t数据到r4-r11，r1地址值自增32 Bytes

    MSR     psp, r1                 ; update stack pointer.用新线程栈最新值 更新到 psp。

pendsv_exit
    ; restore interrupt ，将之前保存到 r2 的 PRIMASK 寄存器值恢复到 PRIMASK 寄存器
    MSR     PRIMASK, r2		; 如果 原来为1则恢复1(开中断)，原来为0则恢复0(继续开中断)。处理中断嵌套问题。
	; lr寄存器为 对应模式下的 r14 连接寄存器，进入异常时自动保存异常返回地址。
	;--------------------------------------------
	;|				 |	特权级		|  用户级	|	CONTROL[0]：0=特权级的线程模式，1=用户级的线程模式，Handler 模式永远都是特权级的。
	;|---------------|--------------|-----------|	CONTROL[1]：堆栈指针选择，0=选择主堆栈指针 MSP（复位后缺省值），1=选择进程堆栈指针 PSP
	;|	异常Handler	 |	handler模式	|  错误用法	|				只有在线程模式，可以使用 PSP。在 handler 模式下，只允许使用 MSP，所以此时不得往该位写 1。
	;|---------------|--------------|-----------|
	;|	主应用程序   |  线程模式	|  线程模式 |
	;|-------------------------------------------
	; handler 模式中， CONTROL[1]总是 0。在线程模式中则可以为 0(MSP) 或 1(PSP)。仅当处于特权级线程模式下，此位才可写，其它场合下禁止写此位。
	; 改变使用堆栈的其它的方式：在异常返回时，通过修改 LR 的位 2，也能实现切换。
	; 在进入异常服务程序后， LR的值被自动更新为特殊的EXC_RETURN，高28位全为1，[3:0]的值有特殊含义:
	; bit3: 0=返回后进入Handler模式 1=返回后进入线程模式
	; bit2: 0=从主堆栈中做出栈操作，返回后使用MSP，1=从进程堆栈中做出栈操作，返回后使用PSP
	; bit1: 1 保留，必须为0
	; bit0: 0=返回ARM状态。1=返回Thumb状态,在CM3中必须为1.
	; 确保异常返回使用的栈指针是 PSP，即 LR 寄存器的位 2 要为 1。因为上电后使用的是MSP，中断handler中也使用MSP，完成task堆栈切换后设置PSP为第一个task的sp指针，之后返回时使用PSP而不返回MSP。
    ORR     lr, lr, #0x04
	; 异常返回，这个时候栈中的剩下内容将会自动加载到 CPU 寄存器：
	; 同时 PSP 的值也将更新，即指向线程栈sp
    BX      lr ; 因用 to_thread->sp 更新 psp，且设置返回使用PSP，则异常返回时处理器会自动把 线程控制块中SP指针处8 uint32_t数据( R0-R3,R12,LR,PC,PSR )弹出堆栈到相应寄存器。
    ; PendSV_Handler 子程序结束
	ENDP


;/*
; * void rt_hw_context_switch(rt_uint32 from ,rt_uint32 to);
; * r0 --> from
; * r1 --> to
; * this fucntion is used to perform the first thread switch,该函数用于非第一次系统调度
; */
rt_hw_context_switch    PROC
    EXPORT rt_hw_context_switch
	
    ; 设置中断标志位 rt_thread_switch_interrupt_flag 为 1
	; 加载 rt_thread_switch_interrupt_flag 的地址到 r2
	LDR r2, =rt_thread_switch_interrupt_flag	; r2 = &rt_thread_switch_interrupt_flag;
	; 加载 rt_thread_switch_interrupt_flag 的值到 r3
	LDR r3, [r2]	; r3 = *rt_thread_switch_interrupt_flag;
	;r3 与 1 比较，相等则执行 BEQ 指令，否则不执行
	CMP r3, #1	; if(r3 == 1)_reswitch
	BEQ _reswitch
	; r3 == 0 ,设置 r3 的值为 1
	MOV r3, #1	; r3 = 1;
	; 将 r3 的值存储到 rt_thread_switch_interrupt_flag，即置 1
	STR r3, [r2]	; *r2 = r3; => rt_thread_switch_interrupt_flag = 1;
	; 设置 rt_interrupt_from_thread 的值
	; 加载 rt_interrupt_from_thread 的地址到 r2
	LDR r2, =rt_interrupt_from_thread
	; 存储 r0 = from sp 的值到 rt_interrupt_from_thread，即上一个线程栈指针 sp 的指针
	STR r0, [r2]
	
_reswitch
	; 设置 rt_interrupt_to_thread 的值
	; 加载 rt_interrupt_from_thread 的地址到 r2
	LDR r2, =rt_interrupt_to_thread
	; 存储 r1 的值到 rt_interrupt_from_thread，即下一个线程栈指针 sp 的指针
	STR r1, [r2]
	; 触发 PendSV 异常，实现上下文切换
	LDR r0, =NVIC_INT_CTRL
	LDR r1, =NVIC_PENDSVSET
	STR r1, [r0]
	; 子程序返回
	BX LR
	; 子程序结束
	ENDP

;/*
; * 函数原型： void rt_hw_context_switch_to(rt_uint32 to);
; * r0 --> to 传入的第一个参数自动放在 R0, r0 = (rt_uint32)&(to_thread->sp)
; * this fucntion is used to perform the first thread switch 该函数用于开启第一次线程切换
; */
rt_hw_context_switch_to    PROC
	; 导出 rt_hw_context_switch_to，让其具有全局属性，可以在 C 文件调用
    EXPORT rt_hw_context_switch_to
    ; set to thread
	; rt_interrupt_to_thread = (rt_uint32)&(to_thread->sp);
    LDR     r1, =rt_interrupt_to_thread 	; r1 = &rt_interrupt_to_thread ;
    STR     r0, [r1]		; *r1 = to; => rt_interrupt_to_thread = (rt_uint32)&(to_thread->sp);

    ; set from thread to 0: 第一次线程切换没有前线程，所以rt_interrupt_from_thread指针置空NULL
	; rt_interrupt_from_thread = 0;
    LDR     r1, =rt_interrupt_from_thread	; r1 = &rt_interrupt_from_thread;
    MOV     r0, #0x0 	; r0 = 0;
    STR     r0, [r1]	; *r1 = r0;

    ; set interrupt flag to 1: 置中断标志位 rt_thread_switch_interrupt_flag 的值为 1
	; rt_thread_switch_interrupt_flag = 1;
    LDR     r1, =rt_thread_switch_interrupt_flag	; r1 = &rt_thread_switch_interrupt_flag;
    MOV     r0, #1		; r0 = 1;
    STR     r0, [r1]	; *r1 = r0;

    ; set the PendSV exception priority 设置 PendSV 异常的优先级
    LDR     r0, =NVIC_SYSPRI2
    LDR     r1, =NVIC_PENDSV_PRI
    LDR.W   r2, [r0,#0x00]       ; read
    ORR     r1,r1,r2             ; modify
    STR     r1, [r0]             ; write-back

    ; trigger the PendSV exception (causes context switch) 触发PendSV异常(软件级)，之后进入中断函数PendSV_Handler
    LDR     r0, =NVIC_INT_CTRL
    LDR     r1, =NVIC_PENDSVSET
    STR     r1, [r0]

    ; enable interrupts at processor level
    CPSIE   F
    CPSIE   I
	
	; 函数调用使用MSP/PSP都可以，这里立刻会进入PendSV_Handler，进行线程上下文切换。而退出PendSV_Handler后，强制使用PSP返回，之后运行的是线程代码。
	; rt_interrupt_to_thread = (rt_uint32)&(to_thread->sp);
	; rt_interrupt_from_thread = 0;
	; rt_thread_switch_interrupt_flag = 1;

    ; never reach here!
    ENDP

    ALIGN   4

    END
